/**
 * @file rsa.c
 * @brief T1 RSA Driver
 *
 * @author yemt@witsi.cn
 * @date 2011-08-13
 * @version T1.AES.01
 */
#include "t1_crypto.h"
#include <string.h>
#include "iodev.h"
#include "ioctl.h"
#include "clk.h"
#include "config.h"

static rsa_key_t * curr_key;

static inline void rsa_in(uint8_t *dst, uint8_t *src, size_t length)
{
        size_t len = RSA_REG_LEN - length;
        dst += RSA_REG_LEN;
        while (len > 0) {
                *(--dst) = 0;
                len--;
        }

        while (length > 0) {
                *(--dst) = *(src++);
                length--;
        }

}

static inline void rsa_out(uint8_t *dst, uint8_t *src, int length)
{
        dst += length;

        while(length-- > 0){
            *(--dst) = *(src++);
        }
}

static void rsa_run(uint8_t *msg, size_t mlen, rsa_key_t *key)
{
        RSA->CCFG = RSA_EN;

        rsa_in(RSA_IN, key->exp, key->explen);
        RSA->CCFG |= RSA_LDEXP;
        while(!(RSA->INT_STA & RSA_LDEXP_DONE));
        RSA->INT_CLR |= RSA_LDEXP_DONE;

        rsa_in(RSA_IN,  msg, mlen);
        RSA->CCFG |= RSA_LDBASE;
        while(!(RSA->INT_STA & RSA_LDBASE_DONE));
        RSA->INT_CLR |= RSA_LDBASE_DONE;

        rsa_in(RSA_IN, key->mod, key->modlen);
        RSA->CCFG |= RSA_START;
        while(!(RSA->INT_STA & RSA_DONE));
        RSA->INT_CLR |= RSA_DONE;
}

static int rsa_init(void)
{
        RSA->INT_EN |= RSA_LDEXP_DONE | RSA_LDBASE_DONE | RSA_DONE;
        RSA->INT_MSK &= ~(RSA_LDEXP_DONE | RSA_LDBASE_DONE | RSA_DONE);
        
        return 0;
}

static ssize_t rsa_read(iodev_t *dev, void *buffer, size_t size)
{
        ssize_t len = curr_key->modlen;

        if (len > size) len = size;

        rsa_out(buffer, RSA_OUT, len);
        memset(RSA_OUT, 0x00, curr_key->modlen);
        return len;
}

static ssize_t rsa_write(iodev_t *dev, const void* buffer, size_t size)
{
        ssize_t len = size;

        if (size > RSA_INLEN)
            len = RSA_INLEN;
        
        rsa_run((uint8_t *)buffer, len, curr_key);

        return len;
}

static int rsa_ioctl(iodev_t *dev, uint32_t cmd, void *arg)
{
        switch (cmd) {
        case IOCTL_CRYPTO_SET_KEY:
                if (arg)
                        curr_key = (rsa_key_t *)arg;
                break;
        case IOCTL_CRYPTO_START:
                __enable_crypto_clk();
                rsa_init();
                break;
        case IOCTL_CRYPTO_STOP:
                __disable_crypto_clk();
                break;
        default:
                break;

        }
        return 0;
}

static const iodev_t rsa_dev __iodev_entry = {
        .name = "rsa",
        .priv = NULL,
        .init = rsa_init,
        .read = rsa_read,
        .write = rsa_write,
        .ioctl = rsa_ioctl,
        .poll = NULL,
#ifdef CONFIG_POWER_CTL
        .suspend = NULL,
        .wakeup = NULL,
#endif
};

